import { mat4, vec2, vec3 } from '@/libs/glmatrix/gl-matrix-min.js'
import { Tween, Easing } from '@tweenjs/tween.js'
import EventEmitter from './EventEmitter.js'
class Flip extends EventEmitter {
  constructor(options) {
    super()
    this.options = options
    this.direction = 0 //0正向 1反向
    this.front = {
      style: {},
      m: [],
      clip: [],
      z: 0 //0正面 1反面
    }
    this.back = {
      style: {},
      m: [],
      clip: [],
      z: 1
    }
    this.now = []
    this.init()
  }
  build(s, e) {
    this.now = {
      s: {
        x: s[0],
        y: s[1],
        z: s[2]
      },
      e: {
        x: e[0],
        y: e[1],
        z: e[2]
      }
    }
    let s0 = this.getOriginPoint(s, this.front.m)
    let e0 = this.getOriginPoint(e, this.front.m)

    /*
    //情况1 e0为左上角
    if (e0[1] < 0 && e0[0] > 0) {
      this.buildLt(s0, e0)
    } else {
      //情况2 e0为左下角
      this.buildLb(s0, e0)
    }
    */
    //统一解法
    let { s1, e1 } = this.fix_point_in_range(s0, e0)
    this.buildCommon(s1, e1)
  }
  fix_point_in_range(s0, e0) {
    let v1 = vec3.create()
    vec3.sub(v1, e0, s0)
    let v2 = [0, e0[1], 0]
    let angle = vec3.angle(v1, v2)
    let cross = vec3.create()
    vec3.cross(cross, v1, v2)
    let deg = (angle / Math.PI) * 180
    //console.log(`deg`, deg, 'v1', v1, 'cross', cross)
    let x, y
    if (deg < 45) {
      if (e0[1] < 0) {
        x = e0[0] + v1[1] //这里有很大问题，方向错误，需要先判断
      } else {
        x = e0[0] - v1[1] //这里有很大问题，方向错误，需要先判断
      }
      y = s0[1]
    } else {
      x = s0[0]
      y = s0[1]
    }
    return {
      s1: [x, y],
      e1: e0
    }
  }
  getOriginPoint(nowPoint, matList) {
    let m = mat4.create()
    let nowMat = matList[0]
    let invert_mat = mat4.create()
    mat4.invert(invert_mat, nowMat)
    let v = vec3.create()
    vec3.transformMat4(v, nowPoint, invert_mat)
    return v
  }
  init() {
    this.w = 200
    this.h = 200
    let { w, h } = this
    this.corrds = {
      lt: [-w / 2, -h / 2, 0],
      lb: [-w / 2, h / 2, 0],
      rt: [w / 2, -h / 2, 0],
      rb: [w / 2, h / 2, 0],
      origin: [0, 0, 0]
    }
    this.ww = window.innerWidth
    this.wh = window.innerHeight
  }
  buildCommon(s0, e0) {
    let { w, h } = this
    let x = (s0[0] + e0[0]) / 2
    let y = (s0[1] + e0[1]) / 2
    let center = [x, y, 0]

    //先处理范围，限定范围，在处理特殊情况
    let k1, k2, b, k2line_x, k2line_y
    //y轴相同
    k1 = (e0[1] - s0[1]) / (e0[0] - s0[0])
    k2 = -1 / k1

    b = y - k2 * center[0]
    k2line_y = (x) => {
      return k2 * x + b
    }
    k2line_x = (y) => {
      return (y - b) / k2
    }

    //上边界 y = -h / 2
    let m_top, m_right, m_left, m_bottom
    if (k1 == 0) {
      m_top = [center[0], -h / 2]
      m_bottom = [center[0], h / 2]
    } else {
      m_top = [k2line_x(-h / 2), -h / 2]
      //右边界 x = w / 2
      m_right = [w / 2, k2line_y(w / 2)]
      //左边界 x = -w / 2
      m_left = [-w / 2, k2line_y(-w / 2)]
      //下边界 y = h / 2
      m_bottom = [k2line_x(h / 2), h / 2]
    }

    let clip_front = []
    let clip_back = []
    let { lt, lb, rt, rb } = this.corrds
    if (s0[0] == e0[0] && s0[1] == e0[1]) {
      clip_front = [lt, lb, rb, rt]
      clip_back = [lt, lt, lt, lt]
    } else {
      if (!m_right || m_right[1] < -h / 2 || m_right[1] > h / 2) {
        clip_front = [lt, m_top, m_bottom, lb]
        clip_back = [m_top, rt, rb, m_bottom]
      } else {
        if (m_top[0] > w / 2) {
          //这里有区别
          clip_front = [lt, rt, m_right, m_bottom, lb]
          clip_back = [m_right, rb, m_bottom]
        } else {
          clip_front = [lt, m_top, m_right, rb, lb]
          clip_back = [m_top, rt, m_right]
        }
      }
    }

    if (this.direction == 0) {
      this.front.clip = clip_front
      this.back.clip = clip_back
    } else {
      this.front.clip = clip_back
      this.back.clip = clip_front
    }
    if (s0[0] == e0[0] && s0[1] == e0[1]) {
    } else {
      //对称操作
      let axis1 = [1, 0, 0]
      let axis2 = vec3.create()

      vec2.sub(axis2, e0, s0)
      let cross = vec3.create()
      let angle = vec3.angle(axis1, axis2)
      vec3.cross(cross, axis1, axis2)
      let deg = (angle / Math.PI) * 180
      //console.log(deg, cross, axis1, axis2)
      if (cross[2] < 0) {
        angle = -angle
      }

      let m = mat4.create()
      //平移原点
      let m1 = mat4.create()
      mat4.translate(m1, m1, center)
      //旋转角度
      let m2 = mat4.create()
      mat4.rotateZ(m2, m2, angle)
      let m3 = mat4.create()
      mat4.mul(m3, m1, m2)
      let m3_i = mat4.create()
      mat4.invert(m3_i, m3)
      //对称
      let m4 = mat4.create()
      mat4.scale(m4, m4, [-1, 1, 1])

      mat4.mul(m, m, m3)
      mat4.mul(m, m, m4)
      mat4.mul(m, m, m3_i)
      if (this.direction == 0) {
        this.back.m[1] = m
      } else {
        this.front.m[1] = m
      }
    }
    this.buildStyle(this.front)
    this.buildStyle(this.back)
  }
  buildStyleAll() {
    this.buildStyle(this.front)
    this.buildStyle(this.back)
  }
  buildStyle(obj) {
    let { w, h } = this
    let m = this.getMat(obj.m, obj.z)
    let style = {}
    let transform = `matrix3d(${m.join(',')})`
    style.transform = transform

    let clip = obj.clip
      .map((res) => {
        let [x, y] = res
        return [x + w / 2, y + h / 2]
      })
      .map((res) => {
        let [x, y] = res
        return x + 'px' + ' ' + y + 'px'
      })
    style['clip-path'] = `polygon(${clip.join(',')})`
    obj.style = style
  }
  getMat(mList, z = 0) {
    let m = mat4.create()
    mList.forEach((res) => {
      mat4.mul(m, m, res)
    })
    if (this.direction == 0) {
      mat4.translate(m, m, [0, 0, z + this.getZ()])
    } else {
      mat4.translate(m, m, [0, 0, 1 - z + this.getZ()])
    }
    return m
  }
  getZ() {
    let { page, total } = this.options
    if (this.isStart) {
      return 9999
    } else {
      if (this.direction == 0) {
        return total * 10 - page * 2
      } else {
        return total * 8 + page * 2
      }
    }
  }
  leftToRight(direction) {
    let isStop = false
    this.direction = 0
    let e = this.now.e
    let s = Object.assign({}, this.now.s)
    s.p = 0
    const tween = new Tween(s, false)
      .to(
        {
          x: direction == 0 ? this.w : -this.w,
          y: e.y,
          z: 0,
          p: 1
        },
        300
      )
      .easing(Easing.Quadratic.InOut)
      .onUpdate(() => {
        this.build([s.x, s.y, s.z], [e.x, e.y, e.z])
        this.emit('update', {
          type: 'leftToRight',
          p: s.p,
          direction
        })
      })
      .onComplete(() => {
        //console.log(s, e)
        isStop = true
        if (direction == 0) {
          this.resetLeft()
        } else {
          this.resetRight()
        }
        this.emit('animated', {
          type: 'leftToRight',
          direction
        })
        console.log(8888)
        this.emit('animatedEnd')
      })
      .start()
    // Setup the animation loop.
    function animate(time) {
      if (!isStop) {
        tween.update(time)
        requestAnimationFrame(animate)
      }
    }
    requestAnimationFrame(animate)
  }
  rightToLeft(direction) {
    this.direction = 1
    let isStop = false
    let e = this.now.e
    let s = Object.assign({}, this.now.s)
    s.p = 0
    const tween = new Tween(s, false)
      .to(
        {
          x: direction == 0 ? -this.w : this.w,
          y: e.y,
          z: 0,
          p: 1
        },
        300
      )
      .easing(Easing.Quadratic.InOut)
      .onUpdate(() => {
        this.build([s.x, s.y, s.z], [e.x, e.y, e.z])
        this.emit('update', {
          type: 'rightToLeft',
          p: s.p,
          direction
        })
      })
      .onComplete(() => {
        //console.log(s, e)
        isStop = true
        if (direction == 0) {
          this.resetRight()
        } else {
          this.resetLeft()
        }
        this.emit('animated', {
          type: 'rightToLeft',
          direction
        })
        this.emit('animatedEnd')
      })
      .start()
    // Setup the animation loop.
    function animate(time) {
      if (!isStop) {
        tween.update(time)
        requestAnimationFrame(animate)
      }
    }
    requestAnimationFrame(animate)
  }

  resetLeft() {
    //console.log(`重置到左边`)
    this.direction = 0
    let { w } = this
    let m = mat4.create()
    mat4.translate(m, m, [w / 2, 0, 0])
    this.front.m = [m]
    this.back.m = [m]
    this.build([w, -w / 2, 0], [w, -w / 2, 0])
  }
  resetRight() {
    //console.log(`重置到右边`)
    this.direction = 1
    let m = mat4.create()
    let { w } = this
    mat4.scale(m, m, [-1, 1, 1])
    mat4.translate(m, m, [w / 2, 0, 0])
    this.front.m = [m]
    this.back.m = [m]
    this.build([-w, -w / 2, 0], [-w, -w / 2, 0])
  }
}

export class FlipUI extends Flip {
  constructor(options) {
    super(options)
    this.resetLeft()
    this.front_img = options.front
    this.back_img = options.back
    this.on('animatedEnd', () => {
      console.log(`animatedEnd`)
      this.isStart = false
      this.isEnd = false
      this.buildStyleAll()
    })
  }
  onStart(e) {
    if (this.isStart) return
    this.s_point = this.getPoint(e)
    this.es = this.getEndPoint(this.s_point)
    let { point, direction, type } = this.es
    if (this.direction == direction) {
      this.isStart = true
      this.isEnd = false
      if (direction == 0) {
        this.resetLeft()
      } else {
        this.resetRight()
      }
    }
  }
  getPoint(e) {
    let { x, y } = e.target.getBoundingClientRect()
    let [touch] = e.touches
    //console.log(touch)
    let { clientX, clientY } = touch
    let point = [clientX, clientY]
    return point
  }
  getEndPoint(s_point) {
    //这是屏幕上的终点坐标，一开始就限定了，记录的也是范围内的坐标
    let { w, h } = this
    let corrds = {
      rt: [w, -h / 2, 0],
      lt: [-w, -h / 2, 0],
      rb: [w, h / 2, 0],
      lb: [-w, h / 2, 0]
    }
    let { ww, wh } = this
    let [x, y] = s_point
    x = x - ww / 2
    y = y - wh / 2
    if (x < 0 && y < 0) {
      return {
        type: 'lt',
        direction: 1,
        point: corrds.lt
      }
    }
    if (x >= 0 && y <= 0) {
      return {
        type: 'rt',
        direction: 0,
        point: corrds.rt
      }
    }
    if (x < 0 && y > 0) {
      return {
        type: 'lb',
        direction: 1,
        point: corrds.lb
      }
    }
    if (x > 0 && y > 0) {
      return {
        type: 'rb',
        direction: 0,
        point: corrds.rb
      }
    }
  }
  onMove(e) {
    if (this.isStart) {
      //console.log(e)
      let mp = this.getPoint(e)
      //console.log(mp)
      let { s_point } = this
      let diff = [mp[0] - s_point[0], mp[1] - s_point[1], 0]
      let s = vec3.create()
      let { point, direction, type } = this.es
      if (type == 'rt') {
        let [dx, dy] = diff
        dx = dx > 0 ? 0 : dx
        dy = dy < 0 ? 0 : dy
        if (dx == 0) dy = 0
        diff = [dx, dy, 0]
        console.log(`dx,dy`, dx, dy, 'type', type, diff)
      }
      if (type == 'rb') {
        let [dx, dy] = diff
        //console.log(`dx,dy`, dx, dy, 'type', type)
        dx = dx > 0 ? 0 : dx
        dy = dy > 0 ? 0 : dy
        diff = [dx, dy, 0]
      }
      if (type == 'lt') {
        let [dx, dy] = diff
        //console.log(`dx,dy`, dx, dy, 'type', type)
        dx = dx < 0 ? 0 : dx
        dy = dy < 0 ? 0 : dy
        diff = [dx, dy, 0]
      }
      if (type == 'lb') {
        let [dx, dy] = diff
        //console.log(`dx,dy`, dx, dy, 'type', type)
        dx = dx < 0 ? 0 : dx
        dy = dy > 0 ? 0 : dy
        diff = [dx, dy, 0]
      }
      //console.log(`diff`, diff)
      vec3.add(s, point, diff)
      //console.log(s, point, 'onMove')
      this.diff = diff
      this.build(s, point)

      if (Math.abs(diff[0]) > (this.w / 4) * 3 || Math.abs(diff[1]) > (this.h / 4) * 3) {
        this.onEnd()
      }
    }
  }
  onEnd(e, options) {
    let { w, h } = this
    if (!this.isEnd) {
      this.isEnd = true
      if (this.isStart) {
        let { point, direction, type } = this.es
        if (type == 'rt' || type == 'rb') {
          let [dx, dy] = this.diff
          if (Math.abs(dx) < (w / 4) * 3 && Math.abs(dy) < (h / 4) * 3) {
            this.leftToRight(0)
          } else {
            this.leftToRight(1)
          }
        }
        if (type == 'lt' || type == 'lb') {
          let [dx, dy] = this.diff
          if (Math.abs(dx) < (w / 4) * 3 && Math.abs(dy) < (h / 4) * 3) {
            this.rightToLeft(0)
          } else {
            this.rightToLeft(1)
          }
        }
        //this.isStart = false
      }
    }
  }
}

export class FlipPage {
  constructor() {
    this.list = []
    this.current = 0
    this.currentFlip = null
    this.isLock = false
    this.style = {}
    this.gm = mat4.create()
    this.init()
  }
  onStart(e) {
    if (this.isLock) return
    let point = this.getPoint(e)
    let [x, y] = point
    let current = this.current
    let w = window.innerWidth
    x = x - w / 2
    let direction = 0
    if (x < 0) {
      direction = 1
    }
    if (direction == 0) {
      this.currentFlip = this.list[current]
      if (current == 0) {
      }
    } else {
      this.currentFlip = this.list[current - 1]
    }
    if (this.currentFlip) {
      this.currentFlip.onStart(e)
      this.isLock = true
    }
  }
  handle(obj) {
    let { type, direction } = obj
    console.log(`direction`, direction)
    if (direction == 0) {
    } else {
      if (type == 'leftToRight') {
        this.current++
      } else {
        this.current--
      }
      console.log('current', this.current)
    }
  }
  onMove(e) {
    if (this.currentFlip) {
      this.currentFlip.onMove(e)
    }
  }
  onEnd(e) {
    if (this.currentFlip) {
      this.currentFlip.onEnd(e)
    }
  }
  getPoint(e) {
    let { x, y } = e.target.getBoundingClientRect()
    let [touch] = e.touches
    //console.log(touch)
    let { clientX, clientY } = touch
    let point = [clientX, clientY]
    return point
  }
  getGM(percent, direction = 0) {
    percent = 1 - percent
    let m = mat4.create()
    if (direction == 1) {
      mat4.translate(this.gm, m, [100 * percent, 0, 0])
    } else {
      mat4.translate(this.gm, m, [-100 * percent, 0, 0])
    }
  }
  buildStyle() {
    this.style = {
      transform: `matrix3d(${this.gm.join(',')})`
    }
  }
  init() {
    let total = 10
    let page = Math.ceil(total / 2)
    for (var i = 0; i < total / 2; i++) {
      let flip = new FlipUI({
        page: i,
        total: 10,
        front: require('../../../images/page-' + (i * 2 + 1) + '.jpg'),
        back: require('../../../images/page-' + (i * 2 + 2) + '.jpg')
      })
      flip.on('animated', (obj) => this.handle(obj))
      flip.on('animatedEnd', () => (this.isLock = false))
      this.list.push(flip)
      //第一页与最后一页单独处理动画效果
      if (i == 0) {
        flip.on('update', (res) => {
          let { direction, type, p } = res
          if (type == 'leftToRight' && direction == 1) {
            this.getGM(p)
          }
          if (type == 'rightToLeft' && direction == 1) {
            this.getGM(1 - p)
          }
          this.buildStyle()
        })
      }
      if (i == page - 1) {
        flip.on('update', (res) => {
          let { direction, type, p } = res
          if (type == 'leftToRight' && direction == 1) {
            this.getGM(1 - p, 1)
          }
          if (type == 'rightToLeft' && direction == 1) {
            this.getGM(p, 1)
          }
          this.buildStyle()
        })
      }
    }
    this.getGM(0, 0)
    this.buildStyle()
  }
}
